:-use_module(library(clpfd)).

% Conjunto de datasets que sao descritos no relatorio
% Chamar no prolog com (substituir dataset1 pelo dataset desejado):
% dataset1(Medicos, Salarios, LimitesDiarios, LimitesSemanais),
%   centro_saude(Medicos, Salarios, LimitesDiarios, LimitesSemanais, Horas, Salario).

% Dataset1 do relatorio (Corresponde aos valores simples, ou seja, sem
% restricoes adicionais)
dataset1([1,2,3,4,5], [60,65,50,70,50], [[8,8,8,8,8,4],
                                         [8,8,8,8,8,4],
                                         [8,8,8,8,8,4],
                                         [8,8,8,8,8,4],
                                         [8,8,8,8,8,4]], [12,9,8,12,6]).

% Dataset2 do relatorio (Corresponde aos valores dados no enunciado)
dataset2([1,2,3,4,5], [60,65,50,70,50], [[0,8,8,8,8,4],
                                         [8,2,8,8,8,4],
                                         [8,8,8,8,8,4],
                                         [8,8,8,8,8,4],
                                         [8,8,8,8,8,0]], [12,9,8,12,6]).

% Dataset3 do relatorio (Corresponde ao conjunto de medicos com
% limites semanais que sao iguais ao numero de horas que o centro de
% saude esta aberto)
dataset3([1,2,3,4,5], [60,65,50,70,50], [[8,8,8,8,8,4],
                                         [8,8,8,8,8,4],
                                         [8,8,8,8,8,4],
                                         [8,8,8,8,8,4],
                                         [8,8,8,8,8,0]], [7,6,9,12,10]).

% Dataset4 do relatorio (mais do que 5 medicos)
dataset4([1,2,3,4,5,6,7], [60,65,50,70,50,70,65], [[8,8,8,8,8,4],
                                                   [8,8,8,8,8,4],
                                                   [8,8,8,8,8,4],
                                                   [8,8,8,8,8,4],
                                                   [8,8,8,8,8,4],
                                                   [8,8,8,8,8,4],
                                                   [8,8,8,8,8,4]], [12,9,8,12,6,5,2]).

% Dataset5 do relatorio (menos do que 5 medicos)
dataset5([1,2,3,4], [60,65,50,70], [[8,8,8,8,8,4],
                                    [8,8,8,8,8,4],
                                    [8,8,8,8,8,4],
                                    [8,8,8,8,8,4]], [12,12,13,16]).

% Dataset6 do relatorio (16 medicos)
dataset6([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16],
         [60,65,50,70,50,60,40,65,70,30,50,61,54,49,60,51],
         [[0,8,8,8,8,4],
          [8,2,8,8,8,4],
          [8,8,8,8,8,4],
          [8,8,8,8,8,4],
          [8,8,8,8,3,4],
          [8,8,8,1,8,4],
          [8,8,3,8,8,4],
          [8,4,8,8,8,4],
          [8,8,8,5,8,4],
          [8,8,3,8,8,4],
          [8,4,8,8,8,4],
          [8,2,8,8,8,4],
          [8,8,3,8,8,2],
          [8,4,8,6,8,4],
          [8,8,8,8,8,4],
          [8,8,8,7,8,0]],
         [12,9,8,12,6,21,10,4,16,10,8,7,9,12,11,13]).

% Dataset7 do relatorio (30 medicos)
dataset7([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30],
         [60,65,50,70,50,60,40,65,70,30,50,61,54,49,60,51,45,52,56,26,32,26,64,58,65,75,25,36,89,90],
         [[0,8,8,8,8,4],
          [8,2,8,8,8,4],
          [8,8,8,8,8,4],
          [8,8,8,8,8,4],
          [8,8,8,8,3,4],
          [8,8,8,1,8,4],
          [8,8,3,8,8,4],
          [8,4,8,8,8,4],
          [8,8,8,5,8,4],
          [8,8,3,8,8,4],
          [4,4,8,8,8,4],
          [8,7,8,5,8,4],
          [8,8,3,6,8,4],
          [8,4,8,8,8,4],
          [8,2,8,8,8,4],
          [8,8,3,8,8,4],
          [8,4,8,8,8,4],
          [8,8,8,5,8,4],
          [8,8,3,8,8,2],
          [8,3,8,6,8,4],
          [8,8,8,7,8,0],
          [8,6,3,8,8,4],
          [8,3,8,5,8,4],
          [8,3,8,5,8,4],
          [8,8,3,8,8,4],
          [8,4,8,8,8,4],
          [8,8,8,5,8,4],
          [8,8,3,8,8,4],
          [8,4,8,8,8,4],
          [8,8,8,5,8,4]],
         [12,9,8,12,6,21,10,4,16,10,8,7,9,12,11,13,10,9,8,6,8,10,30,9,6,8,5,32,12,9]).



centro_saude(Medicos, Salarios, LimitesDiarios, LimitesSemanais, Horas, Salario) :-
    % De Segunda a Sexta o centro de saude esta aberto 8 horas
    % Ao Sabado ele esta aberto 4 horas
    % Numero total de horas que se encontra aberto por semana = 5*8+4 = 44
    length(Horas, 44),

	length(Medicos, TotalMedicos),
	domain(Horas, 1, TotalMedicos),

    limite_horas(Medicos, Horas, LimitesDiarios),

    length(HorasMedicos, TotalMedicos),
    count_horas(Medicos, Horas, HorasMedicos),

    % com estas restricoes garntimos que o custo total e minimo
    calcula_limite_horas(Salarios,HorasMedicos,LimitesSemanais),
    !, % Para evitar que o calcular_limite_horas volte a ser chamado

    calcular_salario(HorasMedicos, Salarios, Salario),
    labeling([ffc, minimize(Salario)], Horas),

    visualizador(Horas, Salario).

limite_horas([], _, []).
limite_horas([Medico|Restantes_medicos], Horas, [LimiteDiarios|Restantes_limites]) :-

    Horas = [Seg1, Seg2, Seg3, Seg4, Seg5, Seg6, Seg7, Seg8,
             Ter1, Ter2, Ter3, Ter4, Ter5, Ter6, Ter7, Ter8,
             Qua1, Qua2, Qua3, Qua4, Qua5, Qua6, Qua7, Qua8,
             Qui1, Qui2, Qui3, Qui4, Qui5, Qui6, Qui7, Qui8,
             Sex1, Sex2, Sex3, Sex4, Sex5, Sex6, Sex7, Sex8,
             Sab1, Sab2, Sab3, Sab4],
    Segunda = [Seg1, Seg2, Seg3, Seg4, Seg5, Seg6, Seg7, Seg8],
    Terca   = [Ter1, Ter2, Ter3, Ter4, Ter5, Ter6, Ter7, Ter8],
    Quarta  = [Qua1, Qua2, Qua3, Qua4, Qua5, Qua6, Qua7, Qua8],
    Quinta  = [Qui1, Qui2, Qui3, Qui4, Qui5, Qui6, Qui7, Qui8],
    Sexta   = [Sex1, Sex2, Sex3, Sex4, Sex5, Sex6, Sex7, Sex8],
    Sabado = [Sab1, Sab2, Sab3, Sab4],

    LimiteDiarios = [LimiteSegunda, LimiteTerca, LimiteQuarta, LimiteQuinta, LimiteSexta, LimiteSabado],

    count(Medico, Segunda, #=, HorasMedicoSEG), HorasMedicoSEG in 0..LimiteSegunda,
    count(Medico, Terca,   #=, HorasMedicoTER), HorasMedicoTER in 0..LimiteTerca,
    count(Medico, Quarta,  #=, HorasMedicoQUA), HorasMedicoQUA in 0..LimiteQuarta,
    count(Medico, Quinta,  #=, HorasMedicoQUI), HorasMedicoQUI in 0..LimiteQuinta,
    count(Medico, Sexta,   #=, HorasMedicoSEX), HorasMedicoSEX in 0..LimiteSexta,
    count(Medico, Sabado,  #=, HorasMedicoSAB), HorasMedicoSAB in 0..LimiteSabado,

    limite_horas(Restantes_medicos, Horas, Restantes_limites).

count_horas([], _, []).
count_horas([Num_medico|Restantes_medicos], Horas, [HorasMedico|Restantes_horas_medicos]) :-
    count(Num_medico, Horas, #=, HorasMedico),
    count_horas(Restantes_medicos, Horas, Restantes_horas_medicos).

calcular_salario([], [], 0).
calcular_salario([HorasMedico|Restantes_horas], [SalarioMedico|Restantes_salarios], Salario) :-
    calcular_salario(Restantes_horas, Restantes_salarios, Salario1),
    Salario is HorasMedico * SalarioMedico + Salario1.

calcula_limite_horas(_,HorasMedicos,LimiteHorasPorSemana):-
	sum(LimiteHorasPorSemana,#=,44),!,
        calcula_limites(HorasMedicos,LimiteHorasPorSemana).

calcula_limite_horas(_,_,LimiteHorasPorSemana):-
	sum(LimiteHorasPorSemana,#<,44),fail.


calcula_limite_horas(Salario,HorasMedicos,LimiteHorasPorSemana):-
	maximum(Caro,Salario),
	element(X,Salario,Caro),
	sum(LimiteHorasPorSemana,#=,Totalhoras),
	RestoHoras #= Totalhoras - 44 #/\ RestoHoras #> 0,
	element(X,LimiteHorasPorSemana,Hora),
	Hora #> RestoHoras,!,
	Temporetirado #= Hora-RestoHoras,
	modifica(X,Salario,0,Salario1),
	modifica(X,LimiteHorasPorSemana,Temporetirado,LimiteHorasPorSemana1),
	calcula_limite_horas(Salario1,HorasMedicos,LimiteHorasPorSemana1).

calcula_limite_horas(Salario,HorasMedicos,LimiteHorasPorSemana):-
	maximum(Caro,Salario),
	element(X,Salario,Caro),
	sum(LimiteHorasPorSemana,#=,Totalhoras),
	RestoHoras #= Totalhoras - 44 #/\ RestoHoras #> 0,
	element(X,LimiteHorasPorSemana,Hora),
	Hora #=< RestoHoras,!,
	modifica(X,Salario,0,Salario1),
	modifica(X,LimiteHorasPorSemana,0,LimiteHorasPorSemana1),
	calcula_limite_horas(Salario1,HorasMedicos,LimiteHorasPorSemana1).


calcula_limites([],[]).
calcula_limites([HorasMedico|Restantes_horas],[LimiteHoras|Restantes_limites]):-
	HorasMedico #= LimiteHoras,
		calcula_limites(Restantes_horas,Restantes_limites).

modifica(_,[],_,[]).
modifica(1, [_|XS], Valor, [Valor|XS]).
modifica(N, [X|XS], Valor, [X|ZS]) :-
	N1 #= N - 1,
        modifica(N1, XS,Valor , ZS).

%% interface grafica
i :- inicio.

inicio :- repeat,
	write('Alocacao de Medicos num Centro de Saude\n'),
	write('Qual dataset deseja utilizar?\n'),
	write(' 1- Dataset1 (valores simples, sem restricoes adicionais)\n'),
	write(' 2- Dataset2 (valores dados no enunciado)\n'),
	write(' 3- Dataset3 (conjunto de medicos com limites semanais que\n sao iguais ao numero de horas que o centro de saude esta aberto)\n'),
	write(' 4- Dataset4 (mais do que 5 medicos)\n'),
    write(' 5- Dataset5 (menos do que 5 medicos)\n'),
    write(' 6- Dataset6 (menos do que 16 medicos)\n'),
    write(' 7- Dataset7 (menos do que 30 medicos)\n'),
	write(' 0- Sair\n'),
	write('\nopcao: '),
	read(C),
	menu(C).  	
	
%% "Switch" das opcoes do menu  %% menu(+Numero, +Tabuleiro)
menu(0) :- !.

menu(1) :- !,
    dataset1(Medicos, Salarios, LimitesDiarios, LimitesSemanais),
	centro_saude(Medicos, Salarios, LimitesDiarios, LimitesSemanais, Horas, Salario).
	
menu(2) :- !,
    dataset2(Medicos, Salarios, LimitesDiarios, LimitesSemanais),
	centro_saude(Medicos, Salarios, LimitesDiarios, LimitesSemanais, Horas, Salario).
	
menu(3) :- !,
    dataset3(Medicos, Salarios, LimitesDiarios, LimitesSemanais),
	centro_saude(Medicos, Salarios, LimitesDiarios, LimitesSemanais, Horas, Salario).

menu(4) :- !,
    dataset4(Medicos, Salarios, LimitesDiarios, LimitesSemanais),
	centro_saude(Medicos, Salarios, LimitesDiarios, LimitesSemanais, Horas, Salario).

menu(5) :- !,
    dataset5(Medicos, Salarios, LimitesDiarios, LimitesSemanais),
	centro_saude(Medicos, Salarios, LimitesDiarios, LimitesSemanais, Horas, Salario).

menu(6) :- !,
    dataset6(Medicos, Salarios, LimitesDiarios, LimitesSemanais),
	centro_saude(Medicos, Salarios, LimitesDiarios, LimitesSemanais, Horas, Salario).

menu(7) :- !,
    dataset7(Medicos, Salarios, LimitesDiarios, LimitesSemanais),
	centro_saude(Medicos, Salarios, LimitesDiarios, LimitesSemanais, Horas, Salario).

visualizador(Horas, Salario) :- 
	Space = '    ',
    Horas = [Seg1, Seg2, Seg3, Seg4, Seg5, Seg6, Seg7, Seg8,
             Ter1, Ter2, Ter3, Ter4, Ter5, Ter6, Ter7, Ter8,
             Qua1, Qua2, Qua3, Qua4, Qua5, Qua6, Qua7, Qua8,
             Qui1, Qui2, Qui3, Qui4, Qui5, Qui6, Qui7, Qui8,
             Sex1, Sex2, Sex3, Sex4, Sex5, Sex6, Sex7, Sex8,
             Sab1, Sab2, Sab3, Sab4],

	write('       Seg. Ter. Qua. Qui. Sex. Sab.\n'),
	write('  8-9   ') ,write(Seg1), write(Space), write(Ter1), write(Space), write(Qua1), write(Space),
				write(Qui1), write(Space), write(Sex1), write(Space), write(Sab1), nl,
	write(' 9-10   ') ,write(Seg2), write(Space), write(Ter2), write(Space), write(Qua2), write(Space),
			write(Qui2), write(Space), write(Sex2), write(Space), write(Sab2), nl,
	write('10-11   ') ,write(Seg3), write(Space), write(Ter3), write(Space), write(Qua3), write(Space),
			write(Qui3), write(Space), write(Sex3), write(Space), write(Sab3), nl,
	write('11-12   ') ,write(Seg4), write(Space), write(Ter4), write(Space), write(Qua4), write(Space),
			write(Qui4), write(Space), write(Sex4), write(Space), write(Sab4), nl,
	write('12-13   ') ,write(Seg5), write(Space), write(Ter5), write(Space), write(Qua5), write(Space),
			write(Qui5), write(Space), write(Sex5), write(Space), nl,
	write('13-14   ') ,write(Seg6), write(Space), write(Ter6), write(Space), write(Qua6), write(Space),
			write(Qui6), write(Space), write(Sex6), write(Space), nl,
	write('14-15   ') ,write(Seg7), write(Space), write(Ter7), write(Space), write(Qua7), write(Space),
			write(Qui7), write(Space), write(Sex7), write(Space), nl,
	write('15-16   ') ,write(Seg8), write(Space), write(Ter8), write(Space), write(Qua8), write(Space),
			write(Qui8), write(Space), write(Sex8), write(Space), nl,
    write(' Total de salario a pagar: '), write(Salario), nl.
            
			
